package com.haidechizi.util.lock.aop;

import com.haidechizi.util.lock.DistributeLock;
import com.haidechizi.util.lock.annotation.Lock;
import com.haidechizi.util.lock.event.LockEventManager;
import com.haidechizi.util.lock.exception.DistributeLockException;
import com.haidechizi.util.lock.holder.DistributeLockHolder;
import com.haidechizi.util.lock.util.ParamUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author: wangdejun
 * @create: 2019-11-12 17:59
 **/
@Slf4j
@Aspect
public class DistributeLockAop {

    @Autowired
    private LockEventManager lockEventManager;

    @Pointcut("@annotation(lock)")
    public void pointcut(Lock lock) {

    }

    @Around("pointcut(lock)")
    public Object around(ProceedingJoinPoint joinPoint, Lock lock) {
        lockEventManager.beforeLock(joinPoint, lock);
        Throwable throwable = null;
        // 获取lockKey
        String lockKey = getKey(joinPoint, lock);
        // 获取锁的类型
        DistributeLock distributeLock = DistributeLockHolder.getLock(lock.type());
        distributeLock.lock(lockKey);
        try {
            return joinPoint.proceed();
        } catch (Throwable e) {
            throwable = e;
            throw new DistributeLockException(e);
        } finally {
            // 解锁
            distributeLock.unlock(lockKey);
            lockEventManager.afterLock(joinPoint, lock, throwable);
        }
    }

    private String getKey(JoinPoint joinPoint, Lock lock) {
        String[] lockKeyExpression = lock.lockKeyExpression();
        String lockKeyPrefix = lock.lockKeyPrefix();
        if (lockKeyExpression.length == 0) {
            return lockKeyPrefix;
        }
        try {

            StringBuilder sb = new StringBuilder();
            sb.append(lockKeyPrefix);
            for (int i = 0; i < lockKeyExpression.length; i++) {
                Object paramValue = ParamUtils.parseExpression(lockKeyExpression[i], joinPoint, lock.lockKeyArgsTypes()[i]);
                sb.append(lock.separator()).append(paramValue);
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
